﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text;
using System.Text.RegularExpressions;

public partial class JaroWinkler
{
    #region Jaro Distance
    private const double defaultMismatchScore = 0.0;
    private const double defaultMatchScore = 1.0;

    [Microsoft.SqlServer.Server.SqlFunction]
    public static System.Data.SqlTypes.SqlDouble SimilarityScore(string source, string target)
    {
        if ((source != null) && (target != null))
        {
            if (source == target)
            {
                return (SqlDouble)defaultMatchScore;
            }
            else
            {
                // calculate the half length of source string (distance for acceptable transpositions)
                int halflen = Math.Min(source.Length, target.Length) / 2 + 1;
                
                //get common characters
                StringBuilder common1 = GetCommonCharacters(source, target, halflen);
                int commonMatches = common1.Length;
                //check for zero in common
                if (commonMatches == 0)
                {
                    return (SqlDouble)defaultMismatchScore;
                }

                StringBuilder common2 = GetCommonCharacters(target, source, halflen);
                //check for same length common strings returning 0.0f is not the same
                if (commonMatches != common2.Length)
                {
                    return (SqlDouble)defaultMismatchScore;
                }

                //get the number of transpositions
                int transpositions = 0;
                for (int i = 0; i < commonMatches; i++)
                {
                    if (common1[i] != common2[i])
                    {
                        transpositions++;
                    }
                }

                int j = 0;
                j += 1;
                
                //calculate jaro metric
                transpositions /= 2;
                double tmp1;
                tmp1 = commonMatches / (3.0 * source.Length) + commonMatches / (3.0 * target.Length) +
                (commonMatches - transpositions) / (3.0 * commonMatches);
                return (SqlDouble)tmp1;
            }
        }
        return (SqlDouble)defaultMismatchScore;
    }

    private static StringBuilder GetCommonCharacters(string source, string target, int distanceSep)
    {
        if ((source != null) && (target != null))
        {
            StringBuilder returnCommons = new StringBuilder(20);
            StringBuilder copy = new StringBuilder(target);
            int firstLen = source.Length;
            int secondLen = target.Length;
            for (int i = 0; i < firstLen; i++)
            {
                char ch = source[i];
                bool foundIt = false;
                for (int j = Math.Max(0, i - distanceSep);
                !foundIt && j < Math.Min(i + distanceSep, secondLen);
                j++)
                {
                    if (copy[j] == ch)
                    {
                        foundIt = true;
                        returnCommons.Append(ch);
                        copy[j] = '#';
                    }
                }
            }
            return returnCommons;
        }
        return null;
    }
    #endregion
    
    #region String Functions
    [Microsoft.SqlServer.Server.SqlFunction]
    public static System.Data.SqlTypes.SqlInt32 FirstIndexOf(string text, string searchWord)
    {
        if ((searchWord == null) ||
        (searchWord.GetType() == typeof(DBNull)))
        {
            searchWord = "";
        }
        if ((text == null) ||
        (text.GetType() == typeof(DBNull)))
        {
            text = "";
        }
        return (SqlInt32)text.IndexOf(searchWord);
    }
    
    [Microsoft.SqlServer.Server.SqlFunction]
    public static System.Data.SqlTypes.SqlInt32 FirstIndexOfPattern(string text, string pattern)
    {
        Regex myRegEx = new Regex(pattern);
        if (!myRegEx.IsMatch(text))
        {
            return -1;
        }
        return myRegEx.Match(text).Index;
    }
    
    [Microsoft.SqlServer.Server.SqlFunction]
    public static System.Data.SqlTypes.SqlInt32 LastIndexOf(string text, string searchWord)
    {
        if ((searchWord == null) ||
        (searchWord.GetType() == typeof(DBNull)))
        {
            searchWord = "";
        }
        if ((text == null) ||
        (text.GetType() == typeof(DBNull)))
        {
            text = "";
        }
        return (SqlInt32)text.LastIndexOf(searchWord);
    }

    [Microsoft.SqlServer.Server.SqlFunction]
    public static System.Data.SqlTypes.SqlBoolean Contains(string text, string searchWord)
    {
        if ((searchWord == null) ||
        (searchWord.GetType() == typeof(DBNull)))
        {
            searchWord = "";
        }
        if ((text == null) ||
        (text.GetType() == typeof(DBNull)))
        {
            text = "";
        }
        return (SqlBoolean)text.Contains(searchWord);
    }
    #endregion
};
